// Copyright (c) 2010 Omer Goshen
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#ifndef GAMEOBJECT_H
#define GAMEOBJECT_H

#include <Ogre.h>
#include <OgreString.h>

#include "core/Countable.h"
#include "core/Serializable.h"
#include "core/Object.h"

#include "components/Component.h"
#include "components/ComponentContainer.h"

using namespace std;
using namespace Ogre;

class Object;
class ComponentContainer;
class BaseGameObject;
class GameObject;
class GameObjectGroup;

//{{{ BaseGameObject
/**
 * \class BaseGameObject
 * \brief
 */
class BaseGameObject :
//{{{ Inherits
	public ::Object,
	public Countable<BaseGameObject>,
	public Serializable,
	public Ogre::GeneralAllocatedObject
//}}}
{
public:
	//{{{ Functions
	inline const size_t         getId()			const			    { return mId; }
	inline const Ogre::String&  getType()			const			{ return mType; }
	inline const Ogre::String&  getName()			const			{ return mName; }

	inline void                 setName(const std::string& name)    { mName = name; }
	inline void                 setName(const char* name)           { mName = name; }

	inline void                 setGroup(GameObjectGroup* g) 		{ mGroup = g; }
	inline void 				_clearGroup() 						{ mGroup = 0; }

	inline Object* 				toObject() 							{ return /*dynamic*/static_cast<Object*>(this); }
	//}}}

protected:
//{{{ Con\Destructors
	BaseGameObject() : Object(),
		mType("GameObject"),
		mId(howMany()),
		mName("Unamed_Object"+StringConverter::toString(howMany())),
		mGroup(0) {}

	BaseGameObject(const Ogre::String& name) : Object(),
		mType("GameObject"),
		mId(howMany()),
		mName(name),
		mGroup(0)  {}

	BaseGameObject(Object params) : Object(),
		mType("GameObject"),
		mId(howMany()),
		mName("GameObject"+Ogre::StringConverter::toString(mId)),
		mGroup(0)
	{
		mName = params.getWithDef<const char*>("name", mName.c_str());
	}
	//}}}

	//{{{ Members
	const Ogre::String 	mType;
	const size_t 		mId;
	Ogre::String 		mName;

	// Group this object belongs to
	GameObjectGroup*	mGroup;
	//}}}
};
//}}}

//{{{ GameObject
/**
 * \class GameObject
 * \author gershon
 * \date 08/01/2010
 * \file GameObject.h
 * \brief
 */
class GameObject :
	public BaseGameObject,
	public ComponentContainer,
	public UserObjectBindings
{
public:
	//{{{ Con\Destructors
	GameObject();
	explicit GameObject(const Ogre::String& name);
	GameObject(const Ogre::String& name, SceneNode* node);
	GameObject(const Ogre::String& name, SceneNode* node, Entity* ent);
	GameObject(const Object& params);
	virtual ~GameObject();
	//}}}

	friend std::ostream& 	operator<<(std::ostream& stream, GameObject go)	{ return stream << go.getName() << "[GameObject]"; }

	void init();

	typedef HashMap<Ogre::String, GameObject*> ObjectMap;
	typedef MapIterator<ObjectMap> ObjectIterator;
	typedef ConstMapIterator<ObjectMap> ConstObjectIterator;


	static ObjectMap mObjectsByName;
//	static ObjectMap::iterator getObjectsIterator() { return mObjectsByName.begin(); }

	static GameObject* 	  getByName(const std::string& name)
	{	
//		return mObjectsByName.find() == mObjectsByName.end() ? NULL : 
		return mObjectsByName[name];
	}
	static ObjectIterator getObjectsIterator() { return MapIterator<ObjectMap>(mObjectsByName); }
};
//}}}


//{{{ GameObjectGroup
//!TODO: possibly use a std::set rather than vector?
/**
 * \class GameObjectGroup
 * \author gershon
 * \date 08/01/2010
 * \file GameObject.h
 * \brief
 */
class GameObjectGroup
{
public:
	typedef std::vector<BaseGameObject*> GameObjectVector;
	typedef VectorIterator<GameObjectVector> GameObjectIterator;

	GameObjectGroup() : mObjects(0) {}
	~GameObjectGroup() {}

	/** \brief Add to group
	 *
	 * \param go BaseGameObject*
	 * \return void
	 *
	 */
	inline void add(BaseGameObject* go)
	{
		go->setGroup(this);
		mObjects.push_back(go);
	}

	/** \brief Remove from group
	 *
	 * \param go BaseGameObject*
	 * \return void
	 *
	 */
	inline void remove(BaseGameObject* go)
	{
		std::vector<BaseGameObject*>::const_iterator it =
		    std::find (mObjects.begin (), mObjects.end (), go);

		if(it==mObjects.end()) return;
		std::swap( mObjects[it - mObjects.begin()], mObjects.back() );

		mObjects.back()->_clearGroup();

		mObjects.pop_back();
	}

	/**< \return true if empty */
	inline bool empty() { return mObjects.empty(); }
	inline void clear() { mObjects.clear(); }

	/** \brief Return the combined bounding box for the group **/
	AxisAlignedBox getBoundingBox();

	inline GameObjectIterator getObjectIterator(void) { return VectorIterator<GameObjectVector>(mObjects); }

	GameObjectVector mObjects;
//	std::vector<IGameObject*> mObjects;
};
//}}}

#endif
